import {
  Observable, of, from,
  tap, mergeMap,
} from 'rxjs'


// 路由处理函数 可以是同步也可以是异步
async function routerHandler() {
  console.log('pay...')
  return 'pay'
}

export class Logger1Interceptor {
  // 拦截器中的 intercept 方法可以是同步也可以是异步
  async intercept(_, next) {
    console.log('Before1')
    const now = Date.now() // 当前时间戳
    debugger
    return next.handle().pipe(
      tap({
        next: val => console.log(`After1... ${Date.now() - now}ms`, val)
      }),
    )
  }
}

export class Logger2Interceptor {
  // 拦截器中的 intercept 方法可以是同步也可以是异步
  intercept(_, next) {
    console.log('Before2')
    const now = Date.now() // 当前时间戳
    debugger
    return next.handle().pipe(
      tap({
        next: val => console.log(`After2... ${Date.now() - now}ms`, val)
      }),
    )
  }
}

const logger1Interceptor = new Logger1Interceptor()
const logger2Interceptor = new Logger2Interceptor()

/**
 * 接收一个拦截器组成的数组 依次执行数组里面的每一个拦截器
 */
function callInterceptors(interceptors) {
  const nextFn = (i = 0): Observable<any> => {
    if (i === interceptors.length) { // 所有的前置拦截器执行完毕后
      let result = routerHandler() // 获取路由处理函数 获取路由处理函数的返回值
      // from 接收一个参数 返回一个可观察对象 如果接收的参数是Promise则返回的可观察对象会生产发送Promise中的值
      // of 接收一个参数 返回一个可观察对象 可观察对象会生产发送该参数
      // 为什么不直接使用 from 来包装路由处理函数返回值？ 因为 from 参数如果是一个数组 则返回的可观察对象会依次生产发送数组中每个元素
      return result instanceof Promise ? from(result) : of(result)
    }
    // 根据 i 索引执行当前拦截器逻辑 时并传递2个参数 null + { handle:() => nextFn(i + 1) } 对象
    // 其中对象里面有一个 handle 属性 该属性的值是一个箭头函数里面包含了下一个拦截器的执行逻辑
    // 如果当前拦截器是一个异步拦截器  相当于是一个promise实例里面包裹可观察对象  Promise<Observable<'pay'>>
    //   然后调用 from 获取 promise 里面的可观察对象 Observable<'pay'> 调用 mergeMap 管道操作符 原样返回 Observable<'pay'>
    //如果当前拦截器是一个同步拦截器 相当于 Observable<'pay'>
    //   上游发送 'pay' 通过 mergeMap 管道操作符中的回调包裹 'pay' 最后返回 Observable<'pay'>
    const result = interceptors[i].intercept(null, { handle:() => nextFn(i + 1) })
    return from(result).pipe(mergeMap( val => val instanceof Observable ? val : of(val))) // 当前拦截器返回的可观察对象发给下一个拦截器使用
  }

  return nextFn()
}

const interceptors = [logger2Interceptor, logger1Interceptor]

debugger
callInterceptors(interceptors)
  .subscribe({
    next: val => console.log('next ', val),
    complete: () => console.log('complete'),
    error: err => console.log('error ', err),
  });

/**
 * mergeMap 操作符执行逻辑
 *   接收一个 project函数 作为参数
 *     project函数接收两个参数 第一个参数是 上游发送的值 第二个参数是 上游发送的值的索引
 *     project函数返回一个可观察对象
 *   mergeMap 会返回一个新的可观察对象 
 *     这个新的可观察对象会同时监听 project函数 返回的每一个 可观察对象 发送的值
 *     每当 project 函数返回的可观察对象发送一个值时 都会被提取作为新的可观察对象发送的值
 * 
 * 路由处理函数可以是同步也可以是异步
 *   如果路由处理函数返回的是一个 Promise实例 则需要使用 from 操作符获取 promise.resolve 的值同时将该值包装到可观察对象中
 *     from操作符接收的参数如果是一个数组 则可观察对象会依次发送数组中每一个元素
 *     from 操作符接收的如果是一个可观察对象 则原样返回这个可观察对象
 *   如果路由处理函数返回的不是一个 Promise实例 则直接使用 of 操作符将值包装成可观察对象
 * 
 * 拦截器的 intercept 方法可以是同步也可以是异步
 *   异步情况 拦截器的 intercept 方法返回的是一个 Promise<Observable<路由处理函数返回值>>
 *     1. 使用 from 操作符解开 Promise 获取 Observable<路由处理函数返回值>
 *     2. 然后 from 返回一个新的可观察对象 Observable<Observable<路由处理函数返回值>>
 *     3. 调用新的可观察对象的 pipe 方法使用 mergeMap 操作符传递一个 project函数
 *     4. project函数 接收上游可观察对象发送的 Observable<路由处理函数返回值> 返回这个 Observable<路由处理函数返回值>
 *     5. mergeMap 生成的新可观察对象 会同步监听 project函数返回的可观察对象发送的值 作为 mergeMap 生成的可观察对象发送的值
 *     6. mergeMap 返回的是 Observable<路由处理函数返回值> 给下一个拦截器使用
 *   同步情况 拦截器的 intercept 方法返回的是一个 Observable<路由处理函数的返回值>
 *     1. 使用 from 操作符原样返回 Observable<路由处理函数的返回值>
 *     2. 调用 pipe 方法使用 mergeMap 操作符传递一个 project函数
 *     3. project函数接收上游可观察对象发送的值: 路由处理函数 使用 of 包装
 *     4. mergeMap 生成的新可观察对象 会同步监听 project函数返回的可观察对象发送的值 作为 mergeMap 生成的可观察对象发送的值
 * 
 * 
 * 封装逻辑
 *   1. 使用 Logger1Interceptor 拦截器类实例化一个 logger1Interceptor 拦截器实例
 *   2. 使用 Logger2Interceptor 拦截器类实例化一个 logger2Interceptor 拦截器实例
 *   3. 将所有的拦截器实例组成一个数组 interceptors
 *   4. 调用 callInterceptors 函数时将拦截器数组作为参数传递 并且 callInterceptors 函数返回的可观察对象调用 subscribe
 *     4.1 定义一个 nextFn 函数 该函数接收一个参数 i 用于指定执行拦截器数组中的哪个拦截器
 *     4.2 执行 nextFn(0) 函数 
 *       4.2.1 执行 logger2Interceptor.intercept 方法 开始处理第一个拦截器的前置逻辑
 *       4.2.2 执行 next.handle() 函数 跳转到 4.3 步骤
 *       4.2.3 调用 from 方法返回一个新的可观察对象 Observable<Observable<'pay'>>
 *       4.2.4 调用可观察对象的 pipe 方法时传递 mergeMap 操作符 返回一个新的可观察对象 Observable<'pay'>
 *       4.2.5 调用可观察对象的 pipe 方法传递 tap 操作符执行第一个拦截器的后置逻辑 返回一个新的可观察对象 Observable<'pay'>
 *       4.2.6 由于 logger2Interceptor.intercept 是同步方法 调用 from 原样返回可观察对象再调用 mergeMap 返回一个新的
 *       4.2.7 此时 nextFn(0) 执行完毕 返回 可观察对象 Observable<'pay'> 
 *     4.3 执行 nextFn(1) 函数
 *       4.3.1 执行 logger1Interceptor.intercept ( 异步 async )方法 开始处理第二个拦截器的前置逻辑
 *       4.3.2 执行 next.handle() 函数 跳转到 4.4 步骤
 *       4.3.3 调用 可观察对象的 pipe 方法 传递 tap 操作符执行第二个拦截器后置逻辑 返回一个新的可观察对象 Observable<'pay'>
 *       4.3.4 由于 logger1Interceptor.intercept 是异步方法 返回 Promise<Observable<'pay'>> 回到 4.2.3 步骤
 *     4.4 执行 nextFn(2) 函数 ( 此时所有拦截器的前置逻辑都已经执行完毕 准备执行路由处理函数 )
 *       4.4.1 调用 routerHandler 方法执行路由处理函数 并将 Promise<'pay'> 返回值赋值给 result
 *       4.4.2 调用 from 操作符时传递 result 返回 Observable<'pay'>
 *       4.4.3 返回 Observable<'pay'> 回到 4.3.4 步骤
 * 
 */

